from selenium.common.exceptions import TimeoutException, StaleElementReferenceException
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait

from tools.debug_tools import DebugTools


class BasePage:
    def __init__(self, driver, timeout=10):
        self.driver = driver
        self.timeout = timeout

    def wait_until(self, condition, timeout=None, msg="等待超时"):
        """封装通用显式等待"""
        if timeout is None:
            timeout = self.timeout
        return WebDriverWait(self.driver, timeout).until(condition, msg)

    def find_element(self, locator, timeout=None):
        """
        查找元素（带显式等待）
        :param timeout: 超时时间
        :param locator: (AppiumBy.ID, "xxx") 或 (AppiumBy.XPATH, "xxx")
        """
        try:
            return self.wait_until(EC.presence_of_element_located(locator), timeout,
                                   msg=f"元素未找到: {locator}")
        except TimeoutException:
            raise Exception(f"元素未找到: {locator}")

    def click(self, locator, timeout=None):
        """
        点击元素（等待可点击）
        """
        element = self.wait_until(EC.element_to_be_clickable(locator), timeout,
                                  msg=f"元素不可点击: {locator}")
        element.click()

    def send_keys(self, locator, content, timeout=None, clear_first=True):
        """
        输入内容
        """
        element = self.find_element(locator, timeout)
        if clear_first:
            element.clear()
        element.send_keys(content)

    def get_text(self, locator, timeout=None, retries=3):
        """
        获取元素文本，可以失效重试
        :param locator: 定位方式
        :param timeout: 显式等待时间
        :param retries: 元素失效后的重试次数
        """
        for attempt in range(retries):
            try:
                element = self.find_element(locator, timeout)
                return element.text
            except StaleElementReferenceException:
                if attempt < retries - 1:
                    # 元素失效，重试
                    continue
                else:
                    raise

    def is_visible(self, locator, timeout=None):
        """
        判断元素是否可见
        """
        try:
            self.wait_until(EC.visibility_of_element_located(locator), timeout)
            return True
        except TimeoutException:
            return False

    def back(self):
        """返回上一页"""
        self.driver.back()

    def press_key(self, keycode):
        """
        物理按键
        :param keycode: 按键码（int）
        """
        self.driver.press_keycode(keycode)

    # ========== WebView 切换 ==========
    def switch_to_webview(self, timeout=15):
        """
        自动等待并切换到 WebView
        """
        dbg = DebugTools(self.driver)

        try:
            WebDriverWait(self.driver, timeout).until(
                lambda d: len(d.contexts) > 1,
                message=f"等待 WebView 超时，当前 contexts: {self.driver.contexts}"
            )
            # 获取最新的 contexts
            contexts = self.driver.contexts
            print(f"[INFO] 获取到的 contexts: {contexts}")

            # 默认切到最后一个 WebView
            for ctx in contexts:
                if ctx.startswith("WEBVIEW"):
                    print(f"[INFO] 切换到 WebView: {ctx}")
                    self.driver.switch_to.context(ctx)
                    return

            raise RuntimeError("没有找到 WebView 上下文")
        except TimeoutException as e:
            print(f"[ERROR] 等待 WebView 出现超时")
            dbg.print_contexts()
            dbg.print_page_source()
            raise e

    def switch_to_native(self):
        """切回原生 APP"""
        self.driver.switch_to.context("NATIVE_APP")

    # ========== WebView 操作 ==========
    def wait_webview_element(self, locator: tuple, timeout: int = None):
        """等待 WebView 元素出现"""
        if timeout is None:
            timeout = self.timeout
        return WebDriverWait(self.driver, timeout).until(
            EC.presence_of_element_located(locator)
        )

    def click_webview_element(self, locator: tuple, timeout: int = None):
        """等待并点击 WebView 元素"""
        if timeout is None:
            timeout = self.timeout
        element = WebDriverWait(self.driver, timeout).until(
            EC.element_to_be_clickable(locator)
        )
        element.click()
        return element

    def send_keys_webview_element(self, locator: tuple, text: str, timeout: int = None):
        """等待并输入文本到 WebView 元素"""
        if timeout is None:
            timeout = self.timeout
        element = WebDriverWait(self.driver, timeout).until(
            EC.presence_of_element_located(locator)
        )
        element.clear()
        element.send_keys(text)
        return element
